package com.wt.demo.validateannotation;

import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.metadata.ConstraintDescriptor;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description
 * @Author: wangtao
 * @Date:15:04 2017/9/8
 * @Email:tao8.wang@changhong.com
 */
public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, Collection<?>> {
    @Override
    public void initialize(MyAnnotation myAnnotation) {

    }

    @Override
    public boolean isValid(Collection<?> collection, ConstraintValidatorContext c) {
        //猜想校验返回的message是从这个方法中返回的，ConstraintValidatorContextImpl.getDefaultConstraintMessageTemplate()
        //上诉方法中用到了ConstraintDescriptor，要修改校验返回的信息，就要修改类ConstraintDescriptor的属性attrobutes的值
        ConstraintValidatorContextImpl context = (ConstraintValidatorContextImpl) c;
//        changeMessage(context);
        changeMap(context);
        //如果要校验集合中的元素的属性的话，假定属性是一个实体，我们只需要遍历实体，查找属性上面的注解NotNull等，然后自己判断，
        //将注解的message封装到上诉map中即可
        //NotNull等注解的Validatar实现类的isValid()方法都很简单，当然也可以直接调用来判断。
        //猜想：
        //实体类的每一个属性也是家里注解的，应该可以在这里面再次使用实体类自身进行校验，但是未找到类似的方法或资料，有待研究
        if (collection == null || collection.size() == 0)
            return false;
        return true;
    }

    private void changeMap(ConstraintValidatorContextImpl context) {
        Map m = context.getConstraintDescriptor().getAttributes();
        Set<Map.Entry> mset = m.entrySet();
        //这里如果直接使用map.put("message","")的方式不行，底层使用的是UnmodifiableMap，c此map不能修改，所以这里采用反射强制修改其值
        for (Map.Entry mapEntity : mset) {
            if (mapEntity.getKey().equals("message")) {
                try {
                    //获取到当前entry的class
                    Class clz = mapEntity.getClass();
                    //这里查看clz.getDeclearedFields()的时候发现只有一个名字为e的属性，类型为entry
                    //查看UnmodifiableEntry发现里面有一个属性为e，而类型为Map.rEntry
                    //这里获取属性e
                    Field f = clz.getDeclaredField("e");
                    f.setAccessible(true);
                    //获取到e的值（一个Map.Node），fe
                    Object fe = f.get(mapEntity);
                    //获取到e的值的class
                    Class clz2 = fe.getClass();
                    System.out.println(clz2);
                    //获取fe（e的值）的value属性
                    Field f2 = clz2.getDeclaredField("value");
                    //设置value属性得值
                    f2.setAccessible(true);
                    f2.set(fe, "hahaha");
                } catch (Exception we) {
                    System.out.println("error");
                    we.printStackTrace();
                }
            }
        }
        System.out.println(m.get("message"));
        System.out.println(context.getConstraintDescriptor().getAttributes().get("message"));
    }

    private void changeMessage(ConstraintValidatorContextImpl context) {
        //了解上面修改map的方式之后我们来看一下另外一种方式
        //查看ConstraintDescriptor的实现类ConstraintDescriptorImpl(只有这一个实现类)，
        //发现该类中的属性全部都不可以set，所以这里我们只能采用反射的方式强制改变其属性attributs的值
        Map m = context.getConstraintDescriptor().getAttributes();
        Set<String> mset = m.keySet();
        Map<String, Object> mm = new HashMap();
        for (String str : mset) {
            mm.put(str, m.get(str));
        }
        mm.put("message", "hahaha");
        System.out.println(m.get("message"));
        ConstraintDescriptor constraintDescriptor = context.getConstraintDescriptor();
        Class clz = constraintDescriptor.getClass();
        try {
            Field f = clz.getDeclaredField("attributes");
            f.setAccessible(true);
            f.set(constraintDescriptor, mm);
        } catch (Exception e) {
            System.out.println("error");
            e.printStackTrace();
        }
        System.out.println(context.getConstraintDescriptor().getAttributes().get("message"));
    }
}
